// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

`timescale 1ns/1ns
`default_nettype none

module reg_file_hci
(
    input wire clk,
    input wire clk_50m,
    input wire reset_n,

    input  logic            avm_read,
    output logic [31:0]     avm_readdata,
    output logic            avm_readdatavalid,
    input  logic            avm_write,
    input  logic [31:0]     avm_writedata,
    input  logic [7:0]      avm_address,
    output logic            avm_waitrequest,

    // signals from fsm
    output logic            bus_enable,
    output logic            resume,
    output logic            abort,
    input  logic [31:0]     ibi,
    input  logic            ibi_message_ready,
    output logic            ibi_message_clear,
    input  logic            command_fifo_read_req,
    output logic [31:0]     command_fifo_read_data,
    output logic            command_fifo_empty,
    output logic            command_fifo_full,
    input  logic            clr_read_fifo_dyn_addr_par_pulse,

    input  logic            response_fifo_write_req,
    input  logic [31:0]     response_fifo_write_data,
    output logic            response_fifo_empty,
    output logic            response_fifo_full,

    input  logic            read_fifo_write_req,
    input  logic [31:0]     read_fifo_write_data,
    output logic            read_fifo_empty,
    output logic            read_fifo_full,

    input  logic            write_fifo_read_req,
    output logic [31:0]     write_fifo_read_data,
    output logic            write_fifo_empty,
    output logic            write_fifo_full

);


reg [31:0] control_reg;
reg [31:0] interrupt_reg;

reg [31:0] status;
reg [31:0] cmd_fifo_level_reg;
reg [31:0] read_fifo_level_reg;

logic           command_fifo_write_req;
logic [31:0]    command_fifo_write_data;

logic           response_fifo_read_req;
logic [31:0]    response_fifo_read_data;

logic           read_fifo_read_req;
logic [31:0]    read_fifo_read_data;

logic           write_fifo_write_req;
logic [31:0]    write_fifo_write_data;


// relay registers to synchronize register reads to the fifo which has up to 8 cycles latency on reads
logic [7:0] avm_address_dly;
logic [2:0] avm_address_dly_counter;
logic       avm_read_dly;

logic [2:0] avm_waitrequest_counter;

logic [4:0] clr_read_fifo_dyn_addr_par_nack_dly_counter;
logic       clr_read_fifo_dyn_addr_par_pulse_dly;

localparam PIO_BASE = 8'h70;
localparam HCI_VERSION = 32'h00000100;
localparam CONTROL_OFFSET = 8'h1;
localparam PIO_BASE_OFFSET =  8'hf;
localparam RESPONSE_QUEUE_OFFSET = (PIO_BASE>>2) + 8'h1;
localparam READ_QUEUE_OFFSET = (PIO_BASE>>2) + 8'h2;
localparam IBI_REG_OFFSET = (PIO_BASE>>2) + 8'h3;
localparam COMMAND_QUEUE_OFFSET = (PIO_BASE>>2);
localparam WRITE_QUEUE_OFFSET = (PIO_BASE>>2) + 8'h2;

localparam STATUS_REG_OFFSET = (PIO_BASE>>2) + 8'h8;

assign status[0] = !read_fifo_empty;
assign status[1] = ibi_message_ready; 
assign status[2] = !command_fifo_full;
assign status[3] = !response_fifo_empty;
assign status[31:4] = 28'h0;

always_ff @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        control_reg <= 0;
        write_fifo_write_data <= 32'h0;
        write_fifo_write_req <= 1'b0;
        command_fifo_write_req <= 1'b0;
        command_fifo_write_data <= 32'h0;
    end
    else begin
        write_fifo_write_data <= 32'h0;
        write_fifo_write_req <= 1'b0;
        command_fifo_write_data <= 32'h0;
        command_fifo_write_req <= 1'b0;
        if (avm_write && !avm_waitrequest) begin
            if (avm_address == CONTROL_OFFSET) begin
                {bus_enable, resume, abort} <= avm_writedata[31:29];
            end
            if ((avm_address == COMMAND_QUEUE_OFFSET) && !command_fifo_full) begin
                command_fifo_write_req <= 1'b1;
                command_fifo_write_data <= avm_writedata;
            end else
            if ((avm_address == WRITE_QUEUE_OFFSET) && !write_fifo_full) begin
                write_fifo_write_req <= 1'b1;
                write_fifo_write_data <= avm_writedata;
            end
        end
    end
end

// AVM waitrequest generator

always_ff @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        avm_waitrequest_counter <= 0;
    end
    else begin
        avm_waitrequest_counter <= (avm_waitrequest_counter == 2'h0) ? 2'h0: avm_waitrequest_counter - 1'b1;
        if (avm_write) begin
            avm_waitrequest_counter <= 2'h3; 
        end
    end
end
assign avm_waitrequest = |avm_waitrequest_counter;

always_comb begin
    avm_readdata = 32'h0;
    ibi_message_clear = 1'b0;
    if (avm_readdatavalid) begin
        if (avm_address_dly == 8'h0) begin
            avm_readdata = HCI_VERSION;
        end else 
        if (avm_address_dly == CONTROL_OFFSET) begin
            avm_readdata = {bus_enable, resume, abort, 29'h0};
        end else 
        if (avm_address_dly == PIO_BASE_OFFSET) begin
            avm_readdata = PIO_BASE;
        end else
        if (avm_address_dly == RESPONSE_QUEUE_OFFSET) begin
            avm_readdata = response_fifo_read_data;
        end else
        if (avm_address_dly == READ_QUEUE_OFFSET) begin
            avm_readdata = read_fifo_read_data;
        end else
        if (avm_address_dly == IBI_REG_OFFSET) begin
            avm_readdata = ibi;
            ibi_message_clear = 1;
        end
        else 
        if (avm_address_dly == STATUS_REG_OFFSET) begin
            avm_readdata = status;
        end
    end
end

always_ff @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        avm_address_dly <= 0;
        avm_address_dly_counter <= 3'h0;
    end
    else begin
        avm_address_dly_counter <= (avm_address_dly_counter == 3'h0) ? 3'h0 : avm_address_dly_counter - 1'b1;
        if (avm_read && avm_address_dly_counter == 3'h0) begin
            avm_address_dly_counter <= 3'h7;
            avm_address_dly <= avm_address;
        end
        if (avm_address_dly_counter == 3'h1) begin
            avm_readdatavalid <= 1'b1;
        end 
        else begin
            avm_readdatavalid <= 1'b0;
        end
    end
end


always_ff @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        clr_read_fifo_dyn_addr_par_nack_dly_counter <= 5'h0;
    end
    else begin
        clr_read_fifo_dyn_addr_par_nack_dly_counter <= (clr_read_fifo_dyn_addr_par_nack_dly_counter == 5'h0) ? 5'h0 : clr_read_fifo_dyn_addr_par_nack_dly_counter - 1'b1;
        if (clr_read_fifo_dyn_addr_par_pulse && clr_read_fifo_dyn_addr_par_nack_dly_counter == 5'h0) begin
            clr_read_fifo_dyn_addr_par_nack_dly_counter <= 5'h15;
        end
    end
end

always_ff @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        avm_read_dly <= 0;
        clr_read_fifo_dyn_addr_par_pulse_dly <= 0;
    end else begin 
        avm_read_dly <= avm_read;
        clr_read_fifo_dyn_addr_par_pulse_dly <= clr_read_fifo_dyn_addr_par_pulse;
    end
end
 
assign response_fifo_read_req = avm_read_dly && (avm_address_dly_counter == 3'h7) && (avm_address_dly == RESPONSE_QUEUE_OFFSET);
assign read_fifo_read_req = ((avm_read_dly && (avm_address_dly_counter == 3'h7) && (avm_address_dly == READ_QUEUE_OFFSET)) || (clr_read_fifo_dyn_addr_par_pulse_dly && clr_read_fifo_dyn_addr_par_nack_dly_counter == 5'h15) || (clr_read_fifo_dyn_addr_par_nack_dly_counter == 5'h7 || clr_read_fifo_dyn_addr_par_nack_dly_counter == 5'hE));


four_way_fifo u_4_port_fifo (
    .clk(clk_50m),
    .reset_n(reset_n),

    .port_a_write_req(command_fifo_write_req),
    .port_a_write_data(command_fifo_write_data),
    .port_a_read_req(command_fifo_read_req),
    .port_a_read_data(command_fifo_read_data),
    .port_a_empty(command_fifo_empty),
    .port_a_full(command_fifo_full),

    .port_b_write_req(response_fifo_write_req),
    .port_b_write_data(response_fifo_write_data),
    .port_b_read_req(response_fifo_read_req),
    .port_b_read_data(response_fifo_read_data),
    .port_b_empty(response_fifo_empty),
    .port_b_full(response_fifo_full),

    .port_c_write_req(read_fifo_write_req),
    .port_c_write_data(read_fifo_write_data),
    .port_c_read_req(read_fifo_read_req),
    .port_c_read_data(read_fifo_read_data),
    .port_c_empty(read_fifo_empty),
    .port_c_full(read_fifo_full),

    .port_d_write_req(write_fifo_write_req),
    .port_d_write_data(write_fifo_write_data),
    .port_d_read_req(write_fifo_read_req),
    .port_d_read_data(write_fifo_read_data),
    .port_d_empty(write_fifo_empty),
    .port_d_full(write_fifo_full)
);


endmodule
    
